package alpha.com.google.code.mechanic.janitor.ui;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.FileUtils;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.beans.PojoProperties;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
import org.eclipse.jface.databinding.viewers.ObservableSetContentProvider;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ICheckable;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableColorProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.actions.WorkingSetFilterActionGroup;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.internal.navigator.actions.CommonActionDescriptorManager;
import org.eclipse.ui.internal.navigator.actions.CommonActionProviderDescriptor;
import org.eclipse.ui.internal.navigator.resources.actions.WorkingSetActionProvider;
import org.eclipse.ui.internal.navigator.workingsets.WorkingSetsContentProvider;
import org.eclipse.ui.navigator.CommonActionProvider;
import org.eclipse.ui.navigator.CommonViewer;
import org.eclipse.ui.navigator.IExtensionStateModel;
import org.eclipse.ui.navigator.INavigatorContentService;
import org.eclipse.ui.navigator.NavigatorActionService;
import org.eclipse.ui.navigator.resources.ProjectExplorer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import alpha.com.google.code.mechanic.janitor.model.ContextsCollector;
import alpha.com.google.code.mechanic.janitor.model.DefaultSysFilePath;
import alpha.com.google.code.mechanic.janitor.model.IContext;
import alpha.com.google.code.mechanic.janitor.model.LaunchConfigTypes;
import alpha.com.google.code.mechanic.janitor.model.ProjectContext;

import com.google.eclipse.mechanic.MechanicService;
import com.google.eclipse.mechanic.ReconcilingPreferencesTask;
import com.google.eclipse.mechanic.Task;
import com.google.eclipse.mechanic.internal.EpfFileModel;
import com.google.eclipse.mechanic.internal.EpfFileModelWriter;
import com.google.eclipse.mechanic.internal.TaskType;
import com.google.eclipse.mechanic.plugin.core.MechanicLog;
import com.google.eclipse.mechanic.plugin.core.MechanicPlugin;
import com.google.gson.Gson;

/**
 * The dialog is to manage different kinds of configurations belongs to
 * various Contexts.
 * 
 * Last_edited: Oct 14, 2014
 * 
 * @History:
 * 1. Add an if statement in the method List<String> getFilteredCtxs() to fix
 * the bug that when there exists none activated Context, the Combo in the
 * TaskCopyDialog showes nothing instead of showing all user defined Contexts.
 * @author jingjiejiang
 * @Edited: Oct 11, 2014 
 * 
 * @author jingjiejiang
 * @since Jul 30, 2014
 *
 */
public class ContextElemMgtDialog extends Dialog {
  
  // SELECTEDTASK and UNSELECTEDTASK is for showing which task file is selected by the user
  private static final Image SELECTEDTASK = MechanicPlugin.getImageDescriptor("icons/ticking_icon.png").createImage();
  private static final Image UNSELECTEDTASK = MechanicPlugin.getImageDescriptor("icons/non_ticking_icon.png").createImage();
  private static final String FILE_NAME = "contexts.con";
  
  private DataBindingContext m_bindingContext;
  
  //The title of the dialog.
  private static final String CTXELEM_MGT_DIALOG_TITLE = "Context Configurations Management";
  private static final Image RUN_FAVORITES = MechanicPlugin.
      getImageDescriptor("icons/run_favorites.png").createImage();
  
  private static final Image WORKING_SETS_FOLD = MechanicPlugin.
      getImageDescriptor("icons/working_sets_fold.png").createImage();
  private static final Image PROJECT_ICON = MechanicPlugin.
      getImageDescriptor("icons/project_icon.png").createImage();
  
  private static final Image ECLIPSE_APP = MechanicPlugin.
      getImageDescriptor("icons/eclipse_application.gif").createImage();
  private static final Image JAVA_APPLET = MechanicPlugin.
      getImageDescriptor("icons/java_applet.gif").createImage();
  private static final Image JAVA_APP = MechanicPlugin.
      getImageDescriptor("icons/java_app.gif").createImage();
  private static final Image JUNIT = MechanicPlugin.
      getImageDescriptor("icons/junit_launch.gif").createImage();
  private static final Image JUNIT_PLUGIN_TEST = MechanicPlugin.
      getImageDescriptor("icons/junit_plugin_test.gif").createImage();
  private static final Image OSGI_FRAMEWORK = MechanicPlugin.
      getImageDescriptor("icons/osgi_framework.gif").createImage();
  private static final Image OTHER_LAUNCHES = MechanicPlugin.
      getImageDescriptor("icons/other_launches.png").createImage();
  
  private TabFolder tabFolder;
  private TableViewer tblViewerTaskFiles; 
  private Table tblTaskFiles;
  private CheckboxTableViewer chkboxTblKeyPairs;
  private Table tblTaskKeyPairs;
  
  // The MechanicService is to get Tasks information
  private MechanicService mechService;
  private Task selTask;
  private Set<Task> taskSet;
  // The oriKeyPairs is to keep the original values of modified key/value pairs
  // of different Tasks.
  private Map<String, Map<String, String>> oriKeyPairs;
  // The selTaskId is to record the Id of the current selected Task in the
  // "Task Files" table.
  private String selTaskId = "";
  
  private CheckboxTreeViewer cbTreeWorkingSets;
  private TreeViewerColumn trclmnWorkingSets;
  private Button btnSaveWkgSets;
  private Button btnActivSelSets;
  
  private Map<String, List<String>> mapAllWorkingSets = new TreeMap<String, List<String>>();
  // The Wroking Sets that are stored in a Context.
  private List<IWorkingSet> listCtxWorkingSet = new ArrayList<IWorkingSet>();
  
  private CheckboxTreeViewer cbTreeLaunchConfigs;
  private TreeViewerColumn trclmnLaunchConfigs ;
  private Button btnSaveLaunchConfigs;
  
  // The current launch configurations used by Eclipse.
  private ILaunchConfiguration[] sysLaunchConfigs;
  // The list of launch configurations belong to the Run Favorites.
  private List<String> runFavorites = new ArrayList<String> ();
  private Map<String, List<String>> mapAllLaunchConfigs = new TreeMap<String, List<String>>();
  private Map<String, List<String>> mapSysLaunchConfigs = new TreeMap<String, List<String>>();
  private String[] strCtxLaunchConfigs = {};
  // The list of stored Context Launch Configurations that does not exist
  // in the Eclipse anymore.
  private List<String> listChangedCtxConfigs = new ArrayList<String>();
  
  private Label lblContext;
  private Combo comboContext;
  private Composite compoCtxTasks;
  
  // The variable represents the current user defined Contexts.
  private ContextsCollector curContexts;
  private List<String> ctxList;
  // The ID of the activated Context.
  private String activCtxId;
  
  private final FormToolkit formToolkit = new FormToolkit(Display.getDefault());
  
  /**
   * Create the dialog.
   * @param parentShell
   */
  public ContextElemMgtDialog(Shell parentShell, ContextsCollector ctxcollector) {
    super(parentShell);
    
    ctxList = new ArrayList<String>();
    curContexts = ctxcollector; 
    
    oriKeyPairs = new HashMap<String, Map<String, String>>();
    
    loadMechanicTskData();
    initTblTskFilesSelect();
    
  }
  
  private void loadMechanicTskData () {
    
    mechService = (MechanicService) MechanicService.getInstance();
    taskSet = mechService.getAllKnownTasks();
  }
  
  private void initTblTskFilesSelect() {
       
    if (!taskSet.isEmpty()) {
      selTask = taskSet.iterator().next();
      selTaskId = selTask.getId();
    }
  }
  
  /**
   * Load the Working Sets data for the current selected Context, if it has a
   * previous saved Working Sets file (*.psf), load it; otherwise, get the
   * current Eclipse used Working Sets. 
   *
   * @return
   * @throws Exception 
   */
  private IWorkingSet[] loadCtxWorkingSets () throws Exception {
    
    String ctxpath = this.getSelCtxFileDir(comboContext.getText());
    String filename = comboContext.getText() + ".psf";
    filename = ctxpath + File.separator + filename;
    File file = new File(filename);
    
    if(file.exists()) {
      
      try {
        listCtxWorkingSet = readCtxWorkingSets(file);
      } catch (Exception exception) {
        throw exception;
      }
      return listCtxWorkingSet.toArray(new IWorkingSet[listCtxWorkingSet.size()]);
    }
    else {
      IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();    
      
      return manager.getWorkingSets();
    }
  }
  
  /**
   * Read the save Working Sets data from a specified file.
   *
   * @param file
   * @return <code>listsets</code> a list of Working Sets.
   * @throws Exception An exception will be thrown if
   *         1) the named charset is not supported;
   *         2) the specified file does not exist;
   *         3) I/O problem, invalid format or no element.
   */
  private List<IWorkingSet> readCtxWorkingSets (File file) throws Exception {
    
    BufferedReader reader = null;
    List<IWorkingSet> listsets = new ArrayList<IWorkingSet>();
    
    try {
      reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
    } catch (UnsupportedEncodingException e) {
      throw new Exception ("The named charset is not supported: "
        + e.getMessage());
    } catch (FileNotFoundException e) {
      throw new Exception ("The file :" + file + ", does" + " not exist: "
        + e.getMessage());
    }
    
    XMLMemento xmlMemento = getXMLMementoRoot();
    try {
      xmlMemento = XMLMemento.createReadRoot(reader);
    } catch (WorkbenchException e) {
      throw new Exception ("IO problems, invalid format, or no element: "
        + e.getMessage());
    }
    
    IMemento[] mementos = xmlMemento.getChildren("workingSets");
    IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();      
    
    for (int index = 0; index < mementos.length; index ++) {
      IWorkingSet newset = manager.createWorkingSet(mementos[index]);
      listsets.add(newset);
    }
    
    return listsets;
  }
  
  private void initWorkingSetsMap () throws Exception {
    
    IWorkingSet[] allsets = {};
    
    try {
      allsets = loadCtxWorkingSets();
    } catch (Exception error) {
      throw error;
    }
    
    // methods 1: use the method, it will only show the old and new added working sets both owned projects
//    IWorkbenchPage page = (IWorkbenchPage) PlatformUI.getWorkbench().getActiveWorkbenchWindow().
//        getActivePage();
//    ProjectExplorer projExplorer = (ProjectExplorer) page.findView(IPageLayout.ID_PROJECT_EXPLORER);
//    projExplorer.getCommonViewer().setInput(allsets[1]);
    
    for (int index = 0; index < allsets.length; index ++) {
      
      String workingsetid = allsets[index].getName(); 
      if(!mapAllWorkingSets.containsKey(workingsetid)) {
        mapAllWorkingSets.put(workingsetid, new ArrayList<String>());
        
      }
      
      IAdaptable[] adaplist = allsets[index].getElements();
      
      for (IAdaptable adap: adaplist) {
        
        IProject proj = (IProject)adap.getAdapter(IProject.class);
        if ( proj != null) {
          IProjectDescription projdescr;
          try {
            projdescr = proj.getDescription();
            String projid = projdescr.getName();
            mapAllWorkingSets.get(workingsetid).add(projid);
          } catch (CoreException e) {
            throw new Exception ("The project does not exist or the project "
                + "is not open: " + e.getMessage());
          }
        }
      }
    }
    
  }
  
  private void initLaunchConfigsMap () throws Exception {
    
    String ctxpath = this.getSelCtxFileDir(comboContext.getText());
    String filename = comboContext.getText() + ".launch";
    filename = ctxpath + File.separator + filename;
    File file = new File(filename);
    
    if (file.exists()) {
      try {
        strCtxLaunchConfigs = loadCtxLaunchConfigs(file);
      } catch (Exception e) {
        throw new Exception (e);
      }
    }
    
    try {
      readSysLaunchConfigs();
    } catch (Exception error) {
      throw new Exception(error);
    }
    
    // Filter the Launch Configurations got from the Eclipse, only show those
    // stored in the current Context.
    if (strCtxLaunchConfigs.length != 0) {
      filterLaunchConfigs(strCtxLaunchConfigs);
    }
    
  }
  
  /**
   * If the current activated Context has stored a preferences list for its
   * Launch Configurations, only show the saved Launch Configurations.
   * 
   * @param filter
   */
  private void filterLaunchConfigs (String[] filter) {
      
    // Make a copy of the Map, in order to delete elements when the loop is going on.
    Map<String, List<String>> syslaunnchconfigs = new TreeMap<String, List<String>>();
    syslaunnchconfigs.putAll(mapAllLaunchConfigs);
    
    List<String> launchconfigs = new ArrayList<String>();

    for (Map.Entry<String, List<String>> entry: syslaunnchconfigs.entrySet())  {
       
      boolean issavedtype = false;
       
      for (int i = 0; i < filter.length; i ++) {
        if (entry.getKey().equals(filter[i])) {
          issavedtype = true;
          break;
        }
      }
       
      if (false == issavedtype) {
        mapAllLaunchConfigs.remove(entry.getKey());        
      }
    }
    
    // The loop is to find the Launch Configuratios stored in the filter.
    for (int i = 0; i < filter.length; i ++) {
      if (false == mapAllLaunchConfigs.containsKey(filter[i])) {
        launchconfigs.add(filter[i]);
      }
    }
    
    String[] configswithouttype = launchconfigs.toArray(new String[launchconfigs.size()]);
     
    Map<String, List<String>> filterlaunnchconfigs = new TreeMap<String, List<String>>();
    filterlaunnchconfigs.putAll(mapAllLaunchConfigs);
     
    List<String> list = new ArrayList<String>();
    List<String> containedconfigs= new ArrayList<String>();
     
    for (Map.Entry<String, List<String>> entry: filterlaunnchconfigs.entrySet())  {
       
      list.addAll(filterlaunnchconfigs.get(entry.getKey()));
       
      // The algorithm need to improved in the future.
      // The loops is to find whether the stored Launch Configurations can
      // be found in the Eclipse.
      for (int i = 0; i < configswithouttype.length; i++) {
        // boolean issavedconfigs = false;
        
        for (int j = 0; j < list.size(); j ++) {
          if (list.get(j).equals(configswithouttype[i])) {
            // issavedconfigs = true;
            containedconfigs.add(configswithouttype[i]);
            break;
          }
        }
      }
       
      // The loop is to filter the Launch Configurations in the table according
      // to the stored Launch Configurations.
      for (int i = 0; i < list.size(); i ++) {
        boolean issavedconfigs = false;
         
        int j = 0;
        for (; j < configswithouttype.length; j ++) {
          if (list.get(i).equals(configswithouttype[j])) {
            issavedconfigs = true;
            break;
          }
        }
        if (false == issavedconfigs) {
          mapAllLaunchConfigs.get(entry.getKey()).remove(list.get(i));
        }
      }
    }
  
    // The loop is to find out the changed Launch Configurations.
    for (int i = 0; i < configswithouttype.length; i++) {
      boolean iscontained = false;
      
      for (String config:  containedconfigs) {
        if (config.equals(configswithouttype[i])) {
          iscontained =  true;
        }
      }
      if (false == iscontained) {
        listChangedCtxConfigs.add(configswithouttype[i]);
      }
    }
    
  }
  
  /**
   * Get the Launch Configurations from the Eclipse.
   * @throws Exception A CoreException will throw when get the Launch
   * Configuration information from Eclipse.
   *
   */
  private void readSysLaunchConfigs () throws Exception {
    
    ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); 
    ILaunchConfigurationType[] types = manager.getLaunchConfigurationTypes();
    ILaunchConfiguration[] launchconfigs = null;

    for (int index = 0; index < types.length; index ++) {
      
      String typeid = types[index].getName(); 
      if(!mapAllLaunchConfigs.containsKey(typeid)) {
        
        mapAllLaunchConfigs.put(typeid, new ArrayList<String>());
        try {
          launchconfigs = manager.getLaunchConfigurations(types[index]);
        } catch (CoreException e) {
          throw new Exception (e.getMessage());
        }
        
        for (ILaunchConfiguration launchconfig: launchconfigs) {
          if ( launchconfig != null) {
            String launchconfigid = launchconfig.getName();
            mapAllLaunchConfigs.get(typeid).add(launchconfigid);
          }
        }
      }
    }
    
    mapSysLaunchConfigs.putAll(mapAllLaunchConfigs);
  }
  
  /**
   * Load the saved Launch Configurations of current activated Context.
   * 
   * @return The saved Launch Configurations and their types.
   * @throws Exception An exception will be thrown if the file cannot be found,
   * or an I/O error occurs when reading the file.
   *
   */
  private String[] loadCtxLaunchConfigs(File file) throws Exception {
    
    FileInputStream filein = null;
    try {
      filein = new FileInputStream(file);
    } catch (FileNotFoundException error) {
      throw new Exception ("The file: "+ file + ", does not exist: "
        + error.getMessage());
    }
    
    BufferedReader bufreader = new BufferedReader(new InputStreamReader(filein));
    String strdata = "";
    StringBuilder strbuffer = new StringBuilder();
    try {
      while ((strdata = bufreader.readLine()) != null) 
      {
        strbuffer.append(strdata);
      }
      bufreader.close();
    } catch (IOException e) {
      throw new Exception ("An I/O error occurs: " + e.getMessage());
    }
    
    String jsons = strbuffer.toString();
    Gson gson = new Gson();
    
    return gson.fromJson(jsons, String[].class);
  }
  
  /**
   * Initialise the list of launch configurations that represent those have
   * been added to the Run Favorites in Eclipse.
   *
   */
  protected void initRunFavoritsList () {
    
    for (int i = 0; i < sysLaunchConfigs.length; i++) {
      ILaunchConfiguration configuration = sysLaunchConfigs[i];
      
      ILaunchConfigurationWorkingCopy workingCopy;
      try {
        workingCopy = configuration.getWorkingCopy();
        List<?> groups = workingCopy.getAttribute(IDebugUIConstants.ATTR_FAVORITE_GROUPS, (List<?>)null);
        // Only those launch configuration put into Run Favorites has non empty ATTR_FAVORITE_GROUPS attributes.
        if (groups != null) {
          runFavorites.add(configuration.getName());
        }
      } catch (CoreException e) {
        MessageDialog.openError(getParentShell(), "The method getWorkingCopy()"
            + " failed.", e.getMessage());
        return;
      }
      
    }
  }
  
  private void initComboContexts (Composite composite) {
    
    comboContext = new Combo(composite, SWT.READ_ONLY);
    comboContext.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        if (comboContext.getText().equals(activCtxId)) {
          lblContext.setText("Activated Context:");
        }
        else {
          lblContext.setText("Non-activated Context:");
        }
        
        // Refresh the Working Sets data and Launch Configurations data.
        try {
          updateWorkingSets();
        } catch (Exception error) {
          MessageDialog.openError(getParentShell(), "Couldn't update the "
              + "Working Sets information.", error.getMessage());
          return;
        }
        
        try {
          updateLaunchConfigs();
        } catch (Exception error) {
          MessageDialog.openError(getParentShell(), "Couldn't update the "
              + "Launch Configuration data", error.getMessage());
          return;
        }
      }
    });
    comboContext.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    initComboContextList();
  }
  
  /**
   * Initialise the Combo for showing user defined Contexts.
   *
   */
  private void initComboContextList () {
    
    int index = getCurCtxIdList();
    
    if (0 == index) {
      
      // If there is not activated Context, show "No activated Context".
      String[] array = ctxList.toArray(new String[ctxList.size()]);
      comboContext.setItems(array);
      lblContext.setText("Non-activated Context:");
    }
    else {
      comboContext.add(activCtxId);
      comboContext.select(0);
      lblContext.setText("Activated Context:");
    }
  }
  

  
  /**
   * Get the list of IDs of the user defined Contexts and return the
   * (index + 1) if there exist an activated Context.
   *
   */
  private int getCurCtxIdList() {
    
    Set<IContext> ctxset = curContexts.getContextList();
    Iterator<IContext> iterator = ctxset.iterator();
    int index = 0;
    int count = 1;
    
    while (iterator.hasNext()) {
      
      IContext context = iterator.next();
      ctxList.add(context.getName());
      if (context.isSelected()) {
        index = count;
        activCtxId = context.getName();
      };
      count ++;
    }
    
    return index;
  }
  
  /**
   * Get the directory of the current select Context in the Combo.
   * 
   * @param ctxId
   * @return
   */
  protected String getSelCtxFileDir (String ctxId) {
    
    Set<IContext> ctxs = curContexts.getContextList();
    Iterator<IContext> iterator = ctxs.iterator();
    String path = "";
    
    while (iterator.hasNext()) {
      IContext context = iterator.next();
      if (context.getName().equals(ctxId)) {
        path = context.getFileLocation();
      }
    }
    
    return path;
  }
  
  /**
   * Name the ContextElemMgtDialog
   *
   */
  @Override
  protected void configureShell(Shell shell) {
      super.configureShell(shell);
      if (CTXELEM_MGT_DIALOG_TITLE != null) {
        shell.setText(CTXELEM_MGT_DIALOG_TITLE);
      }
  }

  /**
   * Create contents of the dialog.
   *
   * @param parent
   */
  @Override
  protected Control createDialogArea(Composite parent) {
    Composite container = (Composite) super.createDialogArea(parent);
    GridLayout gridLayout = (GridLayout) container.getLayout();
    gridLayout.numColumns = 2;
    
    createLblCtx(container);
    initComboContexts(container);
    
    Composite compoCtxElem = new Composite(container, SWT.NONE);
    GridData gd_compoCtxElem = new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1);
    gd_compoCtxElem.heightHint = 357;
    compoCtxElem.setLayoutData(gd_compoCtxElem);
    
    createTabFolder(compoCtxElem);
    
    TabItem tbtmProperties = new TabItem(tabFolder, SWT.NONE);
    tbtmProperties.setText("Properties");
    
    compoCtxTasks = new Composite(tabFolder, SWT.NONE);
    
    tbtmProperties.setControl(compoCtxTasks);
    createTblTaskFiles(compoCtxTasks);
    createTblTaskFilesClmns();
    
    createTblKeyPair(compoCtxTasks);
    createCompoCtxTaskLbls(compoCtxTasks);
    createTblKeyPairClmns();
    createTblKeyPairsBtns();
 
    
    TabItem tbtmLaunchConfigs = new TabItem(tabFolder, SWT.NONE);
    tbtmLaunchConfigs.setText("Launch Configurations");
    
    Composite compoLaunchConfigs = new Composite(tabFolder, SWT.NONE);
    tbtmLaunchConfigs.setControl(compoLaunchConfigs);
    
    createCbTreeLaunchConfigs(compoLaunchConfigs);
    createTrclmnLuanchConfigs();
    createCbTreeLaunchConfigsBtns(compoLaunchConfigs);
    
    // Create elements in the Tab page for Working Sets management
    TabItem tbtmWorkingSets = new TabItem(tabFolder, SWT.NONE);
    tbtmWorkingSets.setText("Working Sets");
    
    Composite compoWorkingSets = new Composite(tabFolder, SWT.NONE);
    tbtmWorkingSets.setControl(compoWorkingSets);
    
    createCbtreeWorkingSets(compoWorkingSets);
    createWkgSetsMgtPageButtons(compoWorkingSets);
    
    createSaveWorkingSetsBtn(compoWorkingSets);
    
    // Initialise the data of Launch Configuration and Working Sets.
    try {
      initLaunchConfigsMap();
    } catch (Exception error) {
      MessageDialog.openError(getParentShell(), "Couldn't read Launch "
          + "Configuration information from the file", error.getMessage());
    }
    
    try {
      initWorkingSetsMap();
    } catch (Exception error) {
      MessageDialog.openError(getParentShell(), "Couldn't read Working "
          + "Sets information from the file", error.getMessage());
    }
    
    initTblTskFilesBinding();
    initTblKeyPairsBinding(selTask);
    
    initWorkingSetsTreeBinding();
    initLaunchConfigsTreeBinding();
    cbTreeLaunchConfigs.expandAll();
    
    createProptyPageCtxMenu();
    
    return container;
  }
  
  private void createTabFolder (Composite composite) {
    
    tabFolder = new TabFolder(composite, SWT.NONE);
    tabFolder.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        TabItem item = (TabItem)e.item;
        if (item.getText().equals("Working Sets")) {
          
          if (listCtxWorkingSet.size() != 0) {
            List<String> channgedsets = checkWorkingSetsChange(listCtxWorkingSet);
            if (channgedsets.size() != 0) {
              String[] arraynames = channgedsets.toArray(new String[channgedsets.size()]);
              String strnames = " ";
              for (String str: arraynames) {
                strnames = strnames + str + " ";
              }
              Shell pshell = Display.getCurrent().getActiveShell();
              MessageDialog.openWarning(pshell, "Warning!", "The Janitor could not" 
                  + "find the Working Sets:" + strnames + "in the Eclipse."
                  + " The names may have been changed or the Working Sets"
                  + "have already been deleted.");
            }
          }
        }
        else if (item.getText().equals("Launch Configurations")) {
          
          if (strCtxLaunchConfigs.length != 0) {
//            List<String> changedconfigs = checkLaunchConfigsChange();
            if (listChangedCtxConfigs.size() != 0) {
              String[] arrayconfigs = listChangedCtxConfigs.toArray(new String[listChangedCtxConfigs.size()]);
              String strconfigs = " ";
              for (String str: arrayconfigs) {
                strconfigs = strconfigs + str + " ";
              }
              Shell pshell = Display.getCurrent().getActiveShell();
              MessageDialog.openWarning(pshell, "Warning!", "The Janitor could not" 
                  + "find the Launch Configurations:" + strconfigs + "in the"
                  + "Eclipse. The names may have been changed or the Launch"
                  + " Configurations have already been deleted.");
            }
          }
          
        }
      }
    });
    tabFolder.setBounds(0, 0, 615, 360);
  }
  
//  private List<String> checkLaunchConfigsChange () {
//    
//    List<String> changedconfigs = new ArrayList<String>();
//    
//    for (Map.Entry<String, List<String>> storedentry: mapAllLaunchConfigs.entrySet()) {
//      boolean iscontained = false;
//      
//      List<String> storedconfigs = storedentry.getValue();
//      
//      for (String storedconfig: storedconfigs) {
//        List<String> sysconfigs = mapSysLaunchConfigs.get(storedentry.getKey());
//        for (String sysconfig: sysconfigs) {
//          if (sysconfig.equals(storedconfig)) {
//            iscontained = true;
//            break;
//          }
//        }
//        if (false == iscontained) {
//          changedconfigs.add(storedconfig);
//        }
//      }
//    }
//    
//    return changedconfigs;    
//  }
  
  /**
   * The method is to check whether the user has change the name of the Working
   * Set that has been stored in the current Context.
   *
   * @param listWorkingSet The list of Working Set that are stored in the
   *                       current Context.
   * @return The list of Working Set names that do not exist in the Eclipse.
   */
  private List<String> checkWorkingSetsChange (List<IWorkingSet> listWorkingSet) {
    
    IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();  
    IWorkingSet[] sysworkingsets = manager.getWorkingSets();

    List<String> changedsets = new ArrayList<String>();
    
    for (IWorkingSet storedset: listWorkingSet) {
      
      // The variable is to indicate whether the name of the store working set
      // still exist in the Eclipse.
      boolean iscontained = false;
      
      for (IWorkingSet systemset: sysworkingsets) {
        
        if (systemset.getName().equals(storedset.getName())) {
          iscontained = true;
          break;
        }
      }
      
      if (false == iscontained) {
        changedsets.add(storedset.getName());
      }
    }
    
    return changedsets;
  }
  
  private void createProptyPageCtxMenu () {
    
    Menu menu = new Menu(compoCtxTasks);
    compoCtxTasks.setMenu(menu);
    
    initApplyAllMnTm(menu);
    initResOriginMnTm(menu);
    
    new MenuItem(menu, SWT.SEPARATOR);
    
    MenuItem mntmCopy = new MenuItem(menu, SWT.CASCADE);
    mntmCopy.setText("Copy...");
    
    Menu menuCopy = new Menu(mntmCopy);
    mntmCopy.setMenu(menuCopy);
    
    initCopyCtxMnTm(menuCopy);
    initCopyTaskMnTm(menuCopy);
    initCopyKeyPairsMnTm(menuCopy);

    new MenuItem(menu, SWT.SEPARATOR);
    
    initRefreshMnTm(menu);
  }
  
  private void initCopyKeyPairsMnTm (Menu menu) {
    
    MenuItem mntmKeyvaluePairs = new MenuItem(menu, SWT.NONE);
    mntmKeyvaluePairs.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        // If no pairs is selected, warning the user.
        if (chkboxTblKeyPairs.getCheckedElements().length != 0) {
          
          Shell pshell = Display.getCurrent().getActiveShell();
          KeyPairsCopyDialog dialog = new KeyPairsCopyDialog(pshell, taskSet);
          
          int dialogopen = dialog.open();
          if (Dialog.OK == dialogopen) {
            
            if (dialog.isCopyToNewCtx()) {
              
              DefaultSysFilePath syspath = new DefaultSysFilePath();
              // String defpath = syspath.getDefaultSysFilePath();
              String dstnpath = createCxtFilePath(syspath, dialog.getStrCtxName());
              
              boolean success = new File(dstnpath).mkdirs();
              if (!success) {
                MessageDialog.openWarning(pshell, "Error", "The system failed"
                    + " to create a file folder for the Context added before.");
              }
              
              String filename = dialog.getStrTaskFileName() + ".epf";
              Path path = new Path(filename);
              if(path.getFileExtension() == null) {
                filename = filename + ".epf";
              }
              
              String filepath = dstnpath + File.separator + filename;
              File file = new File(filepath);
              try {
                file.createNewFile();
              } catch (IOException error) {
                MessageDialog.openError(getParentShell(), "An I/O error"
                    + " occurred.", error.getMessage());
                return;
              }
              
              EpfFileModel epfFile = new EpfFileModel(dialog.getStrTaskTitle(),
                  dialog.getStrTaskDesc(), getTaskType(dialog.getTaskType()));   
              Object[] selkeys = chkboxTblKeyPairs.getCheckedElements();
              for (Object selkey: selkeys) {
                String key = (String)selkey;
                epfFile.addElement(key, selTask.getKeyPairs().get(key));
              }
              
              IPath taskpath = new Path(filepath);
              
              try {
                EpfFileModelWriter.write(epfFile, taskpath);
              } catch (IOException event) {
                MessageDialog.openError(getShell(), "Error while writing %s", filepath);
                return;
              }

            }
            else {
              
              Object[] seltasks = dialog.getSelTask();
              
              for (Object seltask: seltasks) {
                
                Task task = (Task)seltask;
                Map<String, String> dstnpair = task.getKeyPairs();
                Object[] selkeys = chkboxTblKeyPairs.getCheckedElements();
                
                for (Object selkey: selkeys) {
                  String key = (String)selkey;
                  dstnpair.put(key, selTask.getKeyPairs().get(key));
                }
                
                try {
                  writeEpfTaskFile(task);
                } catch (IOException error) {
                  MessageDialog.openError(getParentShell(), "Couldn't save the"
                      + " task: " + task.getTitle(), error.getMessage());
                }
                
                // Only allow to intert into one selected Task.
                break;
              }
            }
          }
          
          dialog.close();
        }
        else {
          MessageDialog.openWarning(getShell(), "Warning", "Please select the"
              + " key/values pairs that you want to copy first.");
        }
      
      }
    });
    mntmKeyvaluePairs.setText("Key/value pairs");
  }
  
  /**
   * Initialise the menu item (Copy...)"Task", which is to copy all selected
   * Tasks to the destination Context.
   * 
   * @param menu
   */
  private void initCopyTaskMnTm (Menu menu) {
    
    MenuItem mntmTask = new MenuItem(menu, SWT.NONE);
    mntmTask.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        Shell pshell = Display.getCurrent().getActiveShell();
        
        TaskCopyDialog dialog = new TaskCopyDialog(pshell, taskSet, getFilteredCtxs());
        int dialogopen = dialog.open();
        if (Dialog.OK == dialogopen) {
          
          DefaultSysFilePath syspath = new DefaultSysFilePath();
          String defpath = syspath.getDefaultSysFilePath();
          String dstnpath = createCxtFilePath(syspath, dialog.getCopyCtxName());
          if(dialog.isNewCtx()){
            // Create a new file folder for the new Context.
            boolean success = new File(dstnpath).mkdirs();
            if (!success) {
              MessageDialog.openWarning(pshell,  "Error",
                "The system failed to create a file folder for the Context"
                + " added before.");
            }
            
            // Add the new created Context to the context.con file.
            try {
              addProjCxtToFile(dialog.getCopyCtxName(), defpath, dstnpath);
            } catch (IOException error) {
              MessageDialog.openError(getParentShell(), "Couldn't save the "
                  + "Contexts", error.getMessage());
            }
          }
          
          Object[] seltasks = dialog.getSelTaskNames();
          for (Object object: seltasks) {
            Task task = (Task) object;
            String srcpath = getTaskPath(task.getId());
            
            File srcfile = new File(srcpath);
            File dstnfile = new File(dstnpath);
            try {
              FileUtils.copyFileToDirectory(srcfile, dstnfile);
            } catch (IOException error) {
              MessageDialog.openError(getParentShell(), "Couldn't copy "
                + srcfile.toString() + "to the directory "
                + dstnfile.toString(), error.getMessage());
              return;
            }
          }
        }
        
        dialog.close();
      }
    });
    mntmTask.setText("Task");
  }
  
  /**
   * If there exists an activated Context, remove the current selected Context
   * from the Context list in the TaskCopyDialog; otherwise, show all user
   * defined Contexts.
   *
   * @return <code>ctxList</code> All user defined Context
   *         <code>filterctxs</code> All user defined Context except the one
   *         that is currently activated in the Eclipse.
   */
  protected List<String> getFilteredCtxs() {
    
    List<String> filterctxs = new ArrayList<String>();
    
    if (comboContext.getText().equals("")) {
      
      return ctxList;
    }
    else {
      
      for (String ctx: ctxList) {
        if(!ctx.contains(comboContext.getText())) {
          filterctxs.add(ctx);
        }
      }
      return filterctxs;
    }

  }
  
  /**
   * Create a default file path according to a Context name. 
   *
   * @param ctxName
   * @return
   */
  protected String createCxtFilePath (DefaultSysFilePath syspath, String ctxName) {
    
    String dstnpath = "";
    
    syspath.initCtxFileDir();
    dstnpath = syspath.getDefaultSysFilePath()
        + ctxName;
    
    return dstnpath;
  }
  
  /**
   * Initialise the menu item (Copy...)"Context", which is to copy all the
   * files belongs to source Context to the destination Context.
   *
   * @param menu
   */
  private void initCopyCtxMnTm (Menu menu) {
    
    MenuItem mntmContext = new MenuItem(menu, SWT.NONE);
    mntmContext.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        String selctxid = comboContext.getText();
        Shell pshell = Display.getCurrent().getActiveShell();
        ContextCopyDialog dialog = new ContextCopyDialog(pshell, selctxid);
        
        int dialogopen = dialog.open();
        if (dialogopen == Dialog.OK) {
          
          String srcpath = getSelCtxFileDir(selctxid);
          DefaultSysFilePath syspath = new DefaultSysFilePath();
          String defpath = syspath.getDefaultSysFilePath();
          String newctxname = dialog.getDetnCtxName();

          String dstnpath = createCxtFilePath(syspath, dialog.getDetnCtxName());
          
          // Add the new created Context to the context.con file.
          try {
            addProjCxtToFile(newctxname, defpath, dstnpath);
          } catch (IOException error) {
            MessageDialog.openError(getParentShell(), "Couldn't save the "
                + "Contexts", error.getMessage());
          }
          
          // Create a new file folder for the new Context.
          boolean success = new File(dstnpath).mkdirs();
          if (!success) {
            MessageDialog.openWarning(pshell,  "Error",
            "The system failed to create a file folder for the Context"
            + " added before.");
          }
          
          // Copy all files of selected Context to the new Context.
          File source = new File(srcpath);
          File dstn = new File(dstnpath);
          try {
              FileUtils.copyDirectory(source, dstn);
          } catch (IOException error) {
            MessageDialog.openError(getParentShell(), "Couldn't copy files "
                + "from" + srcpath + "to " + dstnpath, error.getMessage());
            return;
          }       
          
        }

        dialog.close();
      }
    });
    mntmContext.setText("Context");
  }
  
  /**
   * Add a new ProjectContext to the context.con file.
   * 
   * @param name The name of the new ProjectContext.
   * @param syspath The directory where the context.con file is.
   * @param cxtpath The directory of the new ProjectContext.
   * @throws IOException 
   *
   */
  protected void addProjCxtToFile (String name, String syspath,
      String cxtpath) throws IOException {
    
    ProjectContext projcxt = createProjCxt(name, cxtpath);
    curContexts.addContext(projcxt);
    try {
      curContexts.serializeContextsInfo(new File(syspath
          + FILE_NAME));
    } catch (IOException e) {
      throw e;
    }
        
  }
  
  protected ProjectContext createProjCxt (String name, String location) {
    
    ProjectContext projcxt = new ProjectContext();
    
    projcxt.setName(name);
    projcxt.setFileLocation(location);
    projcxt.setSelected(false);
    
    return projcxt;
  }
  
  /**
   * Initialise the menu item "Apply All", which is to apply all the changes
   * that users have made to the key/value pairs to the Task files.
   * 
   * @param menu
   */
  private void initApplyAllMnTm (Menu menu) {
    
    MenuItem mntmApplyAll = new MenuItem(menu, SWT.NONE);
    mntmApplyAll.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        for (Task task: taskSet) {
          try {
            writeEpfTaskFile(task);
          } catch (IOException error) {
            MessageDialog.openError(getParentShell(), "Couldn't save the"
                + " task: " + task.getTitle(), error.getMessage());
          }
        }
      }
    });
    mntmApplyAll.setText("Apply all");
  }
  
  /**
   * Initialise the menu item "Refresh", which is to refresh the "Task files"
   * table and the "Key/value pairs" table.
   *
   * @param menu
   */
  private void initRefreshMnTm (Menu menu) {
    
    MenuItem mntmRefresh = new MenuItem(menu, SWT.NONE);
    mntmRefresh.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        updateProptyTables();
        
        try {
          updateWorkingSets();
        } catch (Exception error) {
          MessageDialog.openError(getParentShell(), "Couldn't update the "
              + "Working Sets information.", error.getMessage());
        }
      }
    });
    mntmRefresh.setText("Refresh");
  }
  
  /**
   * The method is to refresh the user defined Context list, the "Task files"
   * table and "Key/value pairs" table.
   *
   */
  private void updateProptyTables() {
    
    mechService = (MechanicService) MechanicService.getInstance();
    taskSet = mechService.getAllKnownTasks();
    
    ctxList.clear();
    initComboContextList();
    
    this.initTblTskFilesSelect();

    initTblTskFilesBinding();
    initTblKeyPairsBinding(selTask);
    //readContextsFile();
    tblTaskFiles.redraw();
  }
  
  private void updateWorkingSets() throws Exception {
    
    mapAllWorkingSets.clear();
    try {
      initWorkingSetsMap();
    } catch (Exception e) {
      throw e;
    }
    initWorkingSetsTreeBinding();
    cbTreeWorkingSets.refresh();
  }
  
  private void updateLaunchConfigs() throws Exception {
    strCtxLaunchConfigs = new String[0];
    mapAllLaunchConfigs.clear();
    try {
      initLaunchConfigsMap();
    } catch (Exception error) {
      throw new Exception (error);
    }
    
    initLaunchConfigsTreeBinding();
    cbTreeLaunchConfigs.refresh();
  }
  
  
  /**
   * Initialise the menu item "Restore default", which is to restore the
   * original key/value pairs of the current selected Task.
   * 
   * @param menu
   */
  private void initResOriginMnTm (Menu menu) {
    
    MenuItem mntmResOrigin = new MenuItem(menu, SWT.NONE);
    mntmResOrigin.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        // Show the original key/value pairs
        resetTaskConfigs();   
        selTask = getSelTaskKeyPairs(selTaskId); 
        initTblKeyPairsBinding(selTask);
        
        if (chkboxTblKeyPairs != null) {
          chkboxTblKeyPairs.refresh();
        }      
      }
    });
    mntmResOrigin.setText("Restore Original");
  }
  
  /**
   * Reset the Key values in the selected Task to theirs original values.
   *
   */
  private void resetTaskConfigs() {
    
    String def_taskid;
    String taskid;
    String def_key;
    String key;
    Task task;
    
    // The key/value pairs that have been saved when Tasks were modified.
    Iterator<String> itr_def_taskid = oriKeyPairs.keySet().iterator();
    // The current Tasks
    Iterator<Task> itr_tasklist;
    Iterator<String> itr_def_key;
    Iterator<String> itr_key;
    
    // check whether the users made any changes first,
    // if itr_def_taskif is empty, finish the method immediately.
    while(itr_def_taskid.hasNext()) {
      
      def_taskid = itr_def_taskid.next();
      itr_tasklist = taskSet.iterator();
      while(itr_tasklist.hasNext()) {
        
        task = itr_tasklist.next();
        taskid = task.getId();    
        //if there are values has been changed in the task file, reset them to default value.
        if(taskid.equals(def_taskid)) {
        
          itr_def_key = oriKeyPairs.get(def_taskid).keySet().iterator();  
          // compare keys stored in oriKeyPairs, with all the keys in the task
          while(itr_def_key.hasNext()) {
            
            def_key = itr_def_key.next();
            itr_key = task.getKeyPairs().keySet().iterator();
            // consider the key paris were all deleted before, hanle the case.
            if (itr_key.hasNext()) {
              while(itr_key.hasNext()) {
                
                // if the value of the key has been changed, reset it
                key = itr_key.next();
                if(key.equals(def_key)) {
                  
                  task.getKeyPairs().remove(key);
                  task.getKeyPairs().put(key, oriKeyPairs.get(def_taskid).
                      get(def_key));
                  break;
                }
                // if the def_key is the one that was deleted before, the in
                // the itr_key, it will not have the same key id, just insert
                // the def_key and its values into the current Task.
                else {
                  task.getKeyPairs().put(def_key, oriKeyPairs.get(def_taskid).
                      get(def_key));
                }
              }
            }
            // all the key/value pairs stored in the Task before were deleted,
            // just insert all the key/values pairs stored in the oriKeyPairs
            // for the cuurrent Task
            else {
              task.getKeyPairs().put(def_key, oriKeyPairs.get(def_taskid).
                  get(def_key));
            }

          }
          break;
        }
      }
    }
    
    oriKeyPairs.clear();
  }
  
  /**
   * Create the labels for the "Properties" Tab page.
   * 
   * @param composite
   */
  private void createCompoCtxTaskLbls (Composite composite) {
    
    Label lblTaskFiles = new Label(composite, SWT.NONE);
    lblTaskFiles.setText("Task files:");
    lblTaskFiles.setBounds(0, 5, 59, 28);
    
    Label lblPropertyValues = new Label(composite, SWT.NONE);
    lblPropertyValues.setText("Key/value pairs:");
    lblPropertyValues.setBounds(252, 5, 94, 14);
    
    Label label_2 = new Label(composite, SWT.SEPARATOR | SWT.VERTICAL);
    label_2.setBounds(248, 0, 2, 322);
    
    Label label_3 = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
    label_3.setBounds(0, 0, 595, 2);
  }
  
  /**
   * Create the "Key/value pairs" table.
   * 
   * @param composite
   */
  private void createTblKeyPair (Composite composite) {
    
    chkboxTblKeyPairs = CheckboxTableViewer.newCheckList(composite, SWT.BORDER | SWT.FULL_SELECTION);
    tblTaskKeyPairs = chkboxTblKeyPairs.getTable();
    tblTaskKeyPairs.setLinesVisible(true);
    tblTaskKeyPairs.setHeaderVisible(true);
    tblTaskKeyPairs.setBounds(252, 25, 343, 265);
  }
  
  /**
   * Create the columns in the "Key/value pairs" table.
   * 
   */
  private void createTblKeyPairClmns () {
    
    TableViewerColumn tblViewerClmnPrefKey = new TableViewerColumn(chkboxTblKeyPairs, SWT.NONE);
    TableColumn tblclmnKey = tblViewerClmnPrefKey.getColumn();
    tblclmnKey.setWidth(250);
    tblclmnKey.setText("      Key");
    
    TableViewerColumn tblViewerClmnPrefValue = new TableViewerColumn(chkboxTblKeyPairs, SWT.NONE);
    TableColumn tblclmnValue = tblViewerClmnPrefValue.getColumn();
    tblclmnValue.setWidth(200);
    tblclmnValue.setText("Value");
    tblViewerClmnPrefValue.setEditingSupport(new ValueItemEditingSupport(chkboxTblKeyPairs));
  }
  
  private void createTblKeyPairsBtns () {
    
    Button btnSelAllKeyPairs = new Button(compoCtxTasks, SWT.NONE);
    btnSelAllKeyPairs.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        chkboxTblKeyPairs.setAllChecked(true);
      }
    });
    btnSelAllKeyPairs.setBounds(252, 294, 94, 28);
    btnSelAllKeyPairs.setText("Select All");
    
    Button btnDeselAllKeyPairs = new Button(compoCtxTasks, SWT.NONE);
    btnDeselAllKeyPairs.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        chkboxTblKeyPairs.setAllChecked(false);
      }
    });
    btnDeselAllKeyPairs.setBounds(342, 294, 94, 28);
    btnDeselAllKeyPairs.setText("Deselect All");
    
    Button btnDelSelKeyPairs = new Button(compoCtxTasks, SWT.NONE);
    btnDelSelKeyPairs.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        Object[] objects = chkboxTblKeyPairs.getCheckedElements();
        
        isGroupTask(getShell());

        // Save the deleted key/value pairs, in case user want to reverse
        // the action and then delete them from the Tasks.
        for (Object object: objects) {
          
          String keyid = object.toString();
          saveDefaultKeyValue(selTaskId, keyid,
              selTask.getKeyPairs().get(keyid));
          selTask.getKeyPairs().remove(keyid);
        }
        
        initTblKeyPairsBinding(selTask);
      }
    });
    btnDelSelKeyPairs.setToolTipText("Delete selected key/value pairs");
    btnDelSelKeyPairs.setBounds(432, 294, 81, 28);
    btnDelSelKeyPairs.setText("Delete");

    Button btnApplyKeyPairsChg = new Button(compoCtxTasks, SWT.NONE);
    btnApplyKeyPairsChg.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        try {
          writeEpfTaskFile(selTask);
        } catch (IOException error) {
          MessageDialog.openError(getParentShell(), "Couldn't save the"
              + " task: " + selTask.getTitle(), error.getMessage());
        }
      }
    });
    btnApplyKeyPairsChg.setToolTipText("Delete selected key/value pairs");
    btnApplyKeyPairsChg.setText("Apply");
    btnApplyKeyPairsChg.setBounds(510, 294, 81, 28);
    formToolkit.adapt(btnApplyKeyPairsChg, true, true);
  }
  
  /**
   * Make the "Value" column in the "Key/value pairs" table editable.
   * 
   * @author jingjiejiang
   * @since Aug 10, 2014
   *
   */
  private class ValueItemEditingSupport extends EditingSupport {
    
    private final TableViewer tableViewer;
    private final CellEditor cellEditor;
    private String key;
    private String keyValue;

    public ValueItemEditingSupport(TableViewer viewer) {
      super(viewer);

      this.tableViewer = viewer;
      this.cellEditor = new TextCellEditor(viewer.getTable());
    }

    @Override
    protected CellEditor getCellEditor(Object element) {
      
      return cellEditor;
    }

    @Override
    protected boolean canEdit(Object element) {

      return true;
    }
    
//    @Override
//    protected void initializeCellEditorValue(CellEditor cellEditor,
//        ViewerCell cell) {
//      rectangle = cell.getTextBounds();
//      isGroupTask();
//      
//      Point dialogpoint = getInitialLocation(getInitialSize());
//      
//      // To get the focus back
//      Robot robot = null;
//      try {
//       robot = new Robot();
//      } catch (AWTException e1) {
//        e1.printStackTrace();
//      }
//     
//      if (robot != null) {
//        robot.mouseMove(dialogpoint.x + rectangle.x + 5,
//            dialogpoint.y + rectangle.y + 5);
//        robot.mousePress(InputEvent.BUTTON1_MASK);
//        robot.mouseRelease(InputEvent.BUTTON1_MASK);
//        robot.delay(200);
//        robot.mousePress(InputEvent.BUTTON1_MASK);
//        robot.mouseRelease(InputEvent.BUTTON1_MASK);
//      }
//    }

    @Override
    protected Object getValue(Object element) {
      
      isGroupTask(cellEditor.getControl().getShell());
//      tableViewer.getControl().setFocus();
//      cellEditor.activate();
//      cellEditor.setFocus();
      
      key = (String) element;
      keyValue = selTask.getKeyPairs().get(key);
      return keyValue;
    }

    @Override
    protected void setValue(Object element, Object value) {
        
      if (!selTaskId.equals("")) {
        saveDefaultKeyValue(selTaskId, key, keyValue);
        selTask.getKeyPairs().remove(key);
        selTask.getKeyPairs().put(key, String.valueOf(value));
        tableViewer.update(element, null);
      }
      else {
        MessageDialog.openWarning(getShell(), "Warning", "You haven't selected"
            + " a Task file yet.");
      }
    }
  }
  
  /**
   * Save the original values of keys after they has been changed.
   *
   * @param taskid The Id of the Task that owns the modified key.
   * @param key The key id of the modified key.
   * @param keyvalue The original value of the modified key.
   */
  private void saveDefaultKeyValue(String taskid, String key, String keyvalue) {
    
    String cur_taskid;
    String cur_key;
    // to record whether the key's value has been added
    boolean has_key = false;
    // if there are key pairs belongs to taskid already exit, add the key pair
    // by using get(taskib).put(...); else, use put(taskid, new HashMap...);
    boolean has_taskid = false;
    Iterator<String> itr_taskid = oriKeyPairs.keySet().iterator();
    Iterator<String> itr_key;
    
    // this is for multiple editing for one key value (has previously saved).
    while(itr_taskid.hasNext()) {
      
      cur_taskid = itr_taskid.next();
      if (cur_taskid.equals(taskid)) {
        has_taskid = true;
      }
      itr_key = oriKeyPairs.get(cur_taskid).keySet().iterator(); 
      while(itr_key.hasNext()) {
        
        cur_key = (String) itr_key.next();
        if (cur_key.equals(key)) {
          
          has_key = true;
          break;
        }
      }
    }
    
    if (false == has_key) {
      if (false == has_taskid) {
        oriKeyPairs.put(taskid, new HashMap<String, String>());
      }
      oriKeyPairs.get(taskid).put(key, keyvalue);
    }
  }
  
  /**
   * Create the "Task files" Table to show Task Files.
   * 
   * @param composite The Composite in the "Properties" Tab page 
   *
   */
  private void createTblTaskFiles (Composite composite) {
    
    tblViewerTaskFiles = new TableViewer(composite, SWT.BORDER | SWT.FULL_SELECTION);
    tblViewerTaskFiles.addSelectionChangedListener(new ISelectionChangedListener() {
      public void selectionChanged(SelectionChangedEvent event) {
        
        IStructuredSelection selection = (IStructuredSelection)event.getSelection();
        Object fstElement = selection.getFirstElement();
        selTaskId = fstElement.toString();
        selTask = getSelTaskKeyPairs(selTaskId); 
        
        // for updating the ticking sign
        initTblTskFilesBinding();
        initTblKeyPairsBinding(selTask);
        
        if (chkboxTblKeyPairs != null) {
          chkboxTblKeyPairs.refresh();
        }
      }
    });
    tblTaskFiles = tblViewerTaskFiles.getTable();
    tblTaskFiles.setLinesVisible(true);
    tblTaskFiles.setHeaderVisible(true);
    tblTaskFiles.setBounds(0, 25, 246, 297);
  }
  
  /**
   * Compare the selected task id with the task IDs in the set. If there exist
   * a match, set the key pairs of the task in the set as an input of the 
   * "Key/value pairs" table.
   * 
   * @param taskId The ID of the selected task in the "Task Files" table.
   * @return return the selected task if it exists.
   *
   */
  private Task getSelTaskKeyPairs(String taskId) {
  
    Task curTask;
    
    Iterator<Task> iterator = taskSet.iterator();
    while (iterator.hasNext()) {
      curTask = iterator.next();
      if (taskId.equals(curTask.getId())) {
        
        return curTask;
      }
    }
      
    return null;
  }
  
  /**
   * Create columns for the "Task files" table.
   */
  private void createTblTaskFilesClmns () {
    
    TableViewerColumn tblViewerClmnTaskName = new TableViewerColumn(tblViewerTaskFiles, SWT.NONE);
    TableColumn tblClmnTaskName = tblViewerClmnTaskName.getColumn();
    tblClmnTaskName.setWidth(100);
    tblClmnTaskName.setText("Name");
    
    TableViewerColumn tblViewerClmnTaskId = new TableViewerColumn(tblViewerTaskFiles, SWT.NONE);
    TableColumn tblClmnTaskId = tblViewerClmnTaskId.getColumn();
    tblClmnTaskId.setWidth(100);
    tblClmnTaskId.setText("ID");
    
    TableViewerColumn tblViewerClmnTaskDesc = new TableViewerColumn(tblViewerTaskFiles, SWT.NONE);
    TableColumn tblClmnTaskDesc = tblViewerClmnTaskDesc.getColumn();
    tblClmnTaskDesc.setWidth(100);
    tblClmnTaskDesc.setText("Description");
  }
  
  /**
   * Create the label for indicating whether a Context listed in the Combo
   * is activated. 
   *
   * @param composite
   *
   */
  private void createLblCtx(Composite composite) {
    
    lblContext = new Label(composite, SWT.NONE);
    lblContext.setAlignment(SWT.RIGHT);
    GridData gd_lblContext = new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1);
    gd_lblContext.widthHint = 133;
    lblContext.setLayoutData(gd_lblContext);
    //lblContext.setText("Non-Activated Context:");
  }
  
  private void initTblTskFilesBinding () {
    
    ObservableSetContentProvider setContentProvider = new ObservableSetContentProvider();
    IObservableMap[] observeMaps = PojoObservables.observeMaps(setContentProvider.getKnownElements(), Task.class, new String[]{"title", "id", "description"});
    tblViewerTaskFiles.setLabelProvider(new TaskObservableMapLabelProvider(observeMaps));
    tblViewerTaskFiles.setContentProvider(setContentProvider);
    
    //tableViewer_Tasklist.setContentProvider(new TaskContentProvider(tableViewer_Tasklist));
    //
    IObservableSet allKnownTasksMechServiceObserveSet = PojoProperties.set("allKnownTasks").observe(mechService);
    tblViewerTaskFiles.setInput(allKnownTasksMechServiceObserveSet);
  }

  private class TaskObservableMapLabelProvider extends ObservableMapLabelProvider {
  
    public TaskObservableMapLabelProvider(IObservableMap[] observeMaps) {
      super(observeMaps);
  
    }
    
    @Override 
    public Image getColumnImage(Object element, int columnIndex) {
      
      Task task = (Task) element;
      Image image = null;
      
      switch (columnIndex) {
      case 0:
        image = isRowSelected(task);
        break;
      case 1:
        break;
      case 2:
        break;
      default:
          break;        
      }
      
      return image;
    }
  }
  
  private Image isRowSelected (Task task) {
    
    Image image = UNSELECTEDTASK;
    
    if (task.getTitle().equals(selTask.getTitle())) {
      return image = SELECTEDTASK;
    }
    else {
      return image;
    }
  }
  
  private void initTblKeyPairsBinding (Task task) {
    
    if (null != task) {
      chkboxTblKeyPairs.setLabelProvider((IBaseLabelProvider) new KeyPairsLabelProvider());
      chkboxTblKeyPairs.setContentProvider(new ArrayContentProvider());
      chkboxTblKeyPairs.setInput(task.getKeyPairs().keySet().toArray());
//      chkboxTblKeyPairs.setAllChecked(true);
    }
  }
  
  private class KeyPairsLabelProvider extends LabelProvider 
  implements ITableLabelProvider, ITableColorProvider {
    public Image getColumnImage(Object element, int columnIndex) {
      return null;
    }

    public String getColumnText(Object element, int columnIndex) {
      String key = (String) element;
      switch (columnIndex) {
        case 0:
          return key;
    
        case 1:
          String value = selTask.getKeyPairs().get(key);
          return value != null ? value : "(null)";
    
        default:
          return null;
      }
    }

    public Color getForeground(Object element, int columnIndex) {
      return null;
    }

    public Color getBackground(Object element, int columnIndex) {
      return null;
    }
    
  }
  
  /**
   * This method is to create the button used in Tab page for Working Sets
   * management: "Select All", "Deselect All", "Activate selected Working Sets"
   * and "Window Working Set".
   *
   */
  private void createWkgSetsMgtPageButtons(Composite composite) {
    
    createSelAllWkgSetsBtn(composite);
    createDeselAllWkgSetsBtn(composite);
    createActivSelWkgSetsBtn(composite);
    createWinWkgSetBtn(composite);
  }
  
  private void createWinWkgSetBtn (Composite composite) {
    
    Button btnWinWkgSets = new Button(composite, SWT.NONE);
    btnWinWkgSets.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        changAllWorkingSetsChkStates(cbTreeWorkingSets, false);
        activSelWorkingSets(null);
      }
    });
    btnWinWkgSets.setBounds(284, 291, 151, 28);
    btnWinWkgSets.setText("Window Working Set");
  }
  
  private void createActivSelWkgSetsBtn (Composite composite) {
    
    btnActivSelSets = new Button(composite, SWT.NONE);
    btnActivSelSets.setToolTipText("Activate selected Working Sets");
    btnActivSelSets.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        activSelWorkingSets(createActivWorkingSet());
      }
    });
    btnActivSelSets.setBounds(190, 291, 94, 28);
    btnActivSelSets.setText("Activate");
    btnActivSelSets.setEnabled(false);
  }
  
  private void createSaveWorkingSetsBtn (Composite composite) {
    
    btnSaveWkgSets = new Button(composite, SWT.NONE);
    btnSaveWkgSets.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent event) {
        
        if (comboContext.getText().equals("")){
          MessageDialog.openWarning(getShell(), "Context Lost", "You haven't"
              + " specify which Context you want the Working Sets"
              + " to store to. Please select a Context from the list first.");
          return;
        }
        
        try {
          saveSelWorkingSetsToCtx(cbTreeWorkingSets.getCheckedElements());
        } catch (Exception error) {
          MessageDialog.openError(getParentShell(), "Couldn't save the "
              + "Working Sets", error.getMessage());
          return;
        }
      }
    });
    btnSaveWkgSets.setToolTipText("Save the selected Working Sets to a Context");
    btnSaveWkgSets.setBounds(436, 291, 94, 28);
    btnSaveWkgSets.setText("Save");
    btnSaveWkgSets.setEnabled(false);
    createTrclmnWorkingSets();
  }
  
  /**
   * Save the selected working sets to the Current activated Context directory.
   * (with suffix .psf)
   * 
   * @param selWorkingSetNames
   * @throws Exception 
   */
  private void saveSelWorkingSetsToCtx (Object[] selWorkingSetNames) throws Exception {
    
    IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();
    IWorkingSet[] allsets = manager.getWorkingSets();
    
    // Construct the Working Set data file (name it as the activated Context
    // name with suffix .psf)
         
    File file;
    
    try {
      file = getCtxElemFilePath(".psf");
    } catch (IOException error) {
      throw error;
    }
    

    BufferedWriter writer = null;
    try {
      writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
    } catch (UnsupportedEncodingException error) {
      throw new UnsupportedEncodingException ("The named encoding UTF-8 is not supported.");
    } catch (FileNotFoundException error) {
      throw new FileNotFoundException ("Couldn't find the file " + file.toString());
    }
    
    XMLMemento xmlMemento = getXMLMementoRoot();
      
    if (allsets != null) {
      for (int index = 0; index < selWorkingSetNames.length; index ++) {
        String setname = selWorkingSetNames[index].toString();
        IMemento memento = xmlMemento.createChild("workingSets"); //$NON-NLS-1$
        
        for (int i = 0; i < allsets.length; i++) {
          if (allsets[i].getName().equals(setname)) {
            allsets[i].saveState(memento);
            break;
          }
        }
      }
    }
    
    try {
      xmlMemento.save(writer);
    } catch (IOException e) {
      throw new IOException ("Couldn't save the Working Sets data to the"
          + " specified file: " + e.getMessage());
    }
  }
  
  /**
   * The whole class is from the file:
   * Eclipse org.eclipse.team.internal.ui.wizards.ProjectSetExportWizard
   * 
   * @return
   */
  private XMLMemento getXMLMementoRoot() {
    Document document;
        try {
            document = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder().newDocument();
            Element element = document.createElement("psf"); //$NON-NLS-1$
            element.setAttribute("version", "2.0"); //$NON-NLS-1$ //$NON-NLS-2$
            document.appendChild(element);
            return new XMLMemento(document, element);
        } catch (ParserConfigurationException e) {
            throw new Error(e.getMessage());
        }
  }
  
  private void createDeselAllWkgSetsBtn (Composite composite) {
    
    Button btnDeselAllWorkingSets = new Button(composite, SWT.NONE);
    btnDeselAllWorkingSets.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        changAllWorkingSetsChkStates(cbTreeWorkingSets, false);
      }
    });
    btnDeselAllWorkingSets.setBounds(95, 291, 94, 28);
    btnDeselAllWorkingSets.setText("Deselect All");
  }
  
  private void createSelAllWkgSetsBtn (Composite composite) {
    
    Button btnSelAllWorkingSets = new Button(composite, SWT.NONE);
    btnSelAllWorkingSets.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        
        changAllWorkingSetsChkStates(cbTreeWorkingSets, true);
      }
    });
    btnSelAllWorkingSets.setBounds(0, 291, 94, 28);
    btnSelAllWorkingSets.setText("Select All");
  }
  
  /**
   * The method is to collect the selected Working Sets IDs.
   */
  private List<String> getSelWorkingSetsIds () {
    
    List<String> setidlist = new ArrayList<String>();
    Object[] elems = cbTreeWorkingSets.getCheckedElements();
    
    for (Object elem: elems) {
      if (mapAllWorkingSets.containsKey(elem.toString())) {
        setidlist.add(elem.toString());
      }
    }
    
    return setidlist;
  }
  

  /**
   * The method is to create a new aggregated Working Set according to the selected
   * Working Sets in the Tab page of Working Sets Management.
   *
   */
  private IWorkingSet createActivWorkingSet () {
    
    IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();
    IWorkingSet[] allsets = manager.getWorkingSets();
//    IWorkingSet[] allsets = manager.getAllWorkingSets();
    List<IWorkingSet> setlist = new ArrayList<IWorkingSet>();
    List<String> setids = getSelWorkingSetsIds();
    String newsetlabel = "Window Working Set";
    String newsetname = "Aggregate for: ";
    
    for (IWorkingSet workingset: allsets) {
      
      for (String str: setids){
          if (workingset.getName().equals(str)) {
            setlist.add(workingset);
          }
      }
    }
    
    newsetlabel = setids.toString();
    newsetname = "Aggregate for: " + newsetlabel;
    IWorkingSet[] selsets = setlist.toArray(new IWorkingSet[setlist.size()]);
    IWorkingSet newset = manager.createAggregateWorkingSet(newsetname, newsetlabel, selsets);

    //activSelWorkingSets(newset);
    return newset;
  }
  
//  private void testWinSet () {
//    
//    IWorkbenchPage page = (IWorkbenchPage) PlatformUI.getWorkbench().getActiveWorkbenchWindow().
//        getActivePage();
//    ProjectExplorer projExplorer = (ProjectExplorer) page.findView(IPageLayout.ID_PROJECT_EXPLORER);
//  
//    IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();
//    IWorkingSet[] allsets = manager.getWorkingSets();
//    
//    String newsetlabel = "Window";
//    String newsetname = "Aggregate for: " + newsetlabel;
//
//    IWorkingSet newset = manager.createAggregateWorkingSet(newsetname, newsetlabel, allsets);
//    projExplorer.getCommonViewer().setInput(newset);
//    projExplorer.setRootMode(ProjectExplorer.PROJECTS);
//  }
  
  
  /**
   * The method is to activate selected Working Sets in the Tab page of Working
   * Sets management, i.e. add them to the Project Explorer.
   */
  private void activSelWorkingSets (IWorkingSet workingset) {
    
    IWorkbenchPage page = (IWorkbenchPage) PlatformUI.getWorkbench().getActiveWorkbenchWindow().
        getActivePage();
    ProjectExplorer projExplorer = (ProjectExplorer) page.findView(IPageLayout.ID_PROJECT_EXPLORER);
//    projExplorer.getCommonViewer().setInput(this.createActivWorkingSet(true));
    projExplorer.setFocus();

    NavigatorActionService actionService = projExplorer
            .getNavigatorActionService();
    CommonViewer viewer = (CommonViewer) projExplorer
            .getAdapter(CommonViewer.class);
    INavigatorContentService contentService = viewer
            .getNavigatorContentService();
    
    try {
        IExtensionStateModel extensionStateModel = contentService
                .findStateModel(WorkingSetsContentProvider.EXTENSION_ID);
        extensionStateModel.setBooleanProperty(
                WorkingSetsContentProvider.SHOW_TOP_LEVEL_WORKING_SETS,
                true);
        projExplorer.setRootMode(ProjectExplorer.WORKING_SETS);

        WorkingSetActionProvider provider = (WorkingSetActionProvider) getActionProvider(
                contentService, actionService,
                WorkingSetActionProvider.class);
        IPropertyChangeListener listener = provider.getFilterChangeListener();
        PropertyChangeEvent pevent = new PropertyChangeEvent(this,
                WorkingSetFilterActionGroup.CHANGE_WORKING_SET, null,
                workingset);
        listener.propertyChange(pevent);

        viewer.refresh();

    } catch (Exception e) {
        e.printStackTrace();
    }
  }
  
  private static CommonActionProvider getActionProvider(
      INavigatorContentService contentService,
      NavigatorActionService actionService, Class cls) throws Exception {

    CommonActionProvider provider = null;
    CommonActionProviderDescriptor[] providerDescriptors = CommonActionDescriptorManager
            .getInstance().findRelevantActionDescriptors(contentService, 
                new ActionContext(new StructuredSelection()));
  
    if (providerDescriptors.length > 0) {
        for (int i = 0; i < providerDescriptors.length; i++) {
            provider = actionService
                    .getActionProviderInstance(providerDescriptors[i]);
            if (provider.getClass() == cls)
                return provider;
        }
    }
    return null;
  }
  
  /**
   * Change all the Working sets check states in the Tab page of Working Sets management.
   */
  private void changAllWorkingSetsChkStates (CheckboxTreeViewer treeViewer, boolean ischecked) {
    
    Tree tree = treeViewer.getTree();
    TreeItem[] items = tree.getItems();
    
    for (TreeItem item: items) {
      
      item.setChecked(ischecked);
    }
  }
  
  private void createTrclmnLuanchConfigs () {
    
    trclmnLaunchConfigs = new TreeViewerColumn(cbTreeLaunchConfigs, SWT.NONE);
    TreeColumn trclmnWorkingSetsAnd = trclmnLaunchConfigs.getColumn();
    trclmnWorkingSetsAnd.setWidth(408);
    trclmnWorkingSetsAnd.setText("      Working Sets and projects");
    trclmnWorkingSetsAnd.setResizable(false);
  }
  
  /**
   * Create the "Launch Configuration" tree.
   * 
   * @param composite
   */
  private void createCbTreeLaunchConfigs (Composite composite) {
    
    cbTreeLaunchConfigs = new CheckboxTreeViewer(composite, SWT.BORDER);
    cbTreeLaunchConfigs.addCheckStateListener(new ICheckStateListener() {
      public void checkStateChanged(CheckStateChangedEvent event) {

//        if (getCheckedLaunchConfigs().length != 0) {
//          
//          btnSaveLaunchConfigs.setEnabled(true);
//        }
//        else {
//          btnSaveLaunchConfigs.setEnabled(false);
//        }
        
        Object selname = event.getElement();
        boolean chkstatus = event.getChecked();
        
        if (mapAllLaunchConfigs.containsKey(selname)) {
          
          changeLaunchConfigTrSubItemCheckState((String)selname, chkstatus);
        }
        else {
          changeLaunchConfigTrPrarentItemCheckState((String)selname);
        }
        
      }
    });

    cbTreeLaunchConfigs.setExpandPreCheckFilters(false);
    Tree treeLaunchConfigs = cbTreeLaunchConfigs.getTree();
    treeLaunchConfigs.setHeaderVisible(true);
    treeLaunchConfigs.setBounds(0, 0, 595, 291);
  }
  
  private void createCbTreeLaunchConfigsBtns (Composite composite) {
    
    Button btnSelAllLaunchConfigs = new Button(composite, SWT.NONE);
    btnSelAllLaunchConfigs.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        changAllLaunchConfigsChkStates(cbTreeLaunchConfigs, true);
      }
    });
    btnSelAllLaunchConfigs.setBounds(0, 294, 94, 28);
    formToolkit.adapt(btnSelAllLaunchConfigs, true, true);
    btnSelAllLaunchConfigs.setText("Select All");
    
    Button btnDeselAllLaunchConfigs = new Button(composite, SWT.NONE);
    btnDeselAllLaunchConfigs.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        changAllLaunchConfigsChkStates(cbTreeLaunchConfigs, false);
      }
    });
    btnDeselAllLaunchConfigs.setBounds(94, 294, 94, 28);
    formToolkit.adapt(btnDeselAllLaunchConfigs, true, true);
    btnDeselAllLaunchConfigs.setText("Deselect All");
    
    btnSaveLaunchConfigs = new Button(composite, SWT.NONE);
    btnSaveLaunchConfigs.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {

        if (comboContext.getText().equals("")){
          MessageDialog.openWarning(getShell(), "Context Lost", "You haven't"
              + " specify which Context you want the Launch Configurations"
              + " to store to. Please select a Context from the list first.");
          return;
        }
        
        if (0 == getCheckedLaunchConfigs().length) {
          MessageDialog.openWarning(getShell(), "Warning", "You haven't"
              + " select the Launch Configurations that you want to store."
              + " Please select them from the list first.");
          return;
        }
        
        // Construct the Launch Configurations data file (name it as the activated Context
        // name with suffix .launch)
        File file = null;
        try {
          file = getCtxElemFilePath(".launch");
        } catch (IOException error) {
          MessageDialog.openError(getParentShell(), "Couldn't save the Launch"
              + " Configurations.", error.getMessage());
          return;
        }
        
        String[] checkedlaunchconfigs = getCheckedLaunchConfigs();
        Gson gson = new Gson();
        String str = gson.toJson(checkedlaunchconfigs);
        
        FileOutputStream fileout = null;
        try {
          fileout = new FileOutputStream(file);
        } catch (FileNotFoundException error) {
          MessageDialog.openError(getParentShell(), "Couldn't find the file "
              + file, error.getMessage());
        }
        OutputStreamWriter writer =new OutputStreamWriter(fileout);
        try {
          writer.append(str);
          writer.close();
          fileout.close();
        } catch (IOException error) {
          MessageDialog.openError(getParentShell(), "An I/O error occured.", error.getMessage());
        }
      }
    });
    btnSaveLaunchConfigs.setBounds(188, 294, 94, 28);
    formToolkit.adapt(btnSaveLaunchConfigs, true, true);
    btnSaveLaunchConfigs.setText("Save");
    
  }
  
  /**
   * The method is to get all the checked Launch Configurations in
   * the cbTreeLaunchConfigs CheckBoxTreeViewer.
   * 
   * @return
   */
  private String[] getCheckedLaunchConfigs() {
    
    List<String> checkedlaunchconfigs = new ArrayList<String>();
    Object[] allcheckeditems = cbTreeLaunchConfigs.getCheckedElements();
    
    for (int index = 0; index < allcheckeditems.length; index ++) {
        checkedlaunchconfigs.add(allcheckeditems[index].toString());
    }
    
    return checkedlaunchconfigs.toArray(new String[checkedlaunchconfigs.size()]);
  }
  
  /**
   * Change all the Working sets check states in the Tab page of Working Sets management.
   */
  private void changAllLaunchConfigsChkStates (CheckboxTreeViewer treeViewer, boolean ischecked) {
    
    Tree tree = treeViewer.getTree();
    TreeItem[] items = tree.getItems();
    
    for (TreeItem item: items) {
      
      item.setChecked(ischecked);
      TreeItem[] children = item.getItems();
      if (children != null) {
        
        for (TreeItem citem: children) {
          citem.setChecked(ischecked);
        }
      }
    }
  }
  
  /**
   * The method is to change the check state of the parent item of the check state 
   * changed sub item.
   */
  private void changeLaunchConfigTrPrarentItemCheckState (String itemid) {

    Tree viewer = cbTreeLaunchConfigs.getTree();
    TreeItem[] parents = viewer.getItems();
    for (TreeItem pitem: parents) {

      // The variable represents the checked sub items of the current 
      // parent item.
      int checkeditems = 0; 
      boolean hasitem = false;
      
      TreeItem[] children = pitem.getItems();
      if (children != null) {
        
        for (TreeItem citem: children) {
          
          String name = citem.getText();
          
          if (name.equals(itemid)) {
            hasitem = true;
          }
          
          if (citem.getChecked()) {
            checkeditems += 1;
          }
        }
        
        // When there are more than one sub items of a parent item is checked,
        // the parent item should keep checked.
        if (hasitem) { 
          if (0 == checkeditems) {
            pitem.setChecked(false);
          }
          else {
            pitem.setChecked(true);
            pitem.setGrayed(true);
          }
        }
        
      }
    }
  }
  
  /**
   * The method is to change the check states of all sub items of the check 
   * state changed parent item.
   */
  private void changeLaunchConfigTrSubItemCheckState(String parentid,
      boolean curchkstate) {
    
    Tree viewer = cbTreeLaunchConfigs.getTree();
    TreeItem[] parents = viewer.getItems();
    for (TreeItem pitem: parents) {
      String name = pitem.getText();
      boolean isexpand = pitem.getExpanded();
      
      if (false == isexpand) {
        pitem.setExpanded(!isexpand);
      }
      
      if (name.contains(parentid)) {
        TreeItem[] children = pitem.getItems();
        for (TreeItem citem: children ) {
          citem.setChecked(curchkstate);
        }
        
        // One selection at a time, so when match, can quit the loop.
        break;
      }
    }
  }
  
  private void createTrclmnWorkingSets () {
    
    trclmnWorkingSets = new TreeViewerColumn(cbTreeWorkingSets, SWT.NONE);
    TreeColumn trclmnTitle = trclmnWorkingSets.getColumn();
    trclmnTitle.setResizable(false);
    trclmnTitle.setWidth(408);
    trclmnTitle.setText("      Working Sets & Projects");
  }
  
  private void createCbtreeWorkingSets (Composite composite) {
    
    cbTreeWorkingSets = new CheckboxTreeViewer(composite, SWT.BORDER);
    cbTreeWorkingSets.addCheckStateListener(new ICheckStateListener() {
      public void checkStateChanged(CheckStateChangedEvent event) {
        
        if (cbTreeWorkingSets.getCheckedElements().length != 0) {
          
          btnSaveWkgSets.setEnabled(true);
          btnActivSelSets.setEnabled(true);
        }
        else {
          btnSaveWkgSets.setEnabled(false);
          btnActivSelSets.setEnabled(false);
        }

        Object setid = event.getElement();
        
        // To make the check states of subitems unchangable. 
        if (!mapAllWorkingSets.containsKey(setid.toString())) {
          ICheckable chkable = event.getCheckable();
          chkable.setChecked(setid, true);
        }
      }
    });

    cbTreeWorkingSets.addTreeListener(new ITreeViewerListener() {
      public void treeCollapsed(TreeExpansionEvent event) {
      }
      public void treeExpanded(TreeExpansionEvent event) {
        
        Object element = event.getElement();
        
        // This is to set parent item gray (expandable one, so only parents)
        // cbTreeWorkingSets.setGrayChecked(element, true).
        setWorkingSetsTrSubTreeCheckState((String)element);
      }
    });
    Tree treeWorkingSets = cbTreeWorkingSets.getTree();
    treeWorkingSets.setBounds(0, 0, 595, 288);
  }
  
  /**
   * The method is to change the check states of all sub items of the check 
   * state changed parent item.
   */
  private void setWorkingSetsTrSubTreeCheckState(String parentid) {
    
    Tree viewer = cbTreeWorkingSets.getTree();
    TreeItem[] parents = viewer.getItems();
    for (TreeItem pitem: parents) {
      String name = pitem.getText();

      if (name.contains(parentid)) {
        TreeItem[] children = pitem.getItems();
        for (TreeItem citem: children ) {
          citem.setChecked(true);
          citem.setGrayed(true);
        }
        
        // One selection at a time, so when match, can quit the loop.
        break;
      }
    }
  }
  
  /**
   * Create contents of the button bar.
   * @param parent
   */
  @Override
  protected void createButtonsForButtonBar(Composite parent) {
    createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
        true);
    createButton(parent, IDialogConstants.CANCEL_ID,
        IDialogConstants.CANCEL_LABEL, false);
  }

  /**
   * Return the initial size of the dialog.
   */
  @Override
  protected Point getInitialSize() {
    return new Point(637, 479);
  }
  
  protected Image isRunFavorite (String configid) {
    
    Image image = null;
    
    Iterator<String> iterator = runFavorites.iterator();
    
    while (iterator.hasNext()) {
      
      String curconfig = iterator.next();
      if (curconfig.equals(configid)) {
        image = RUN_FAVORITES;
      }
    }
    
    return image; 
  }
  
  private void initLaunchConfigsTreeBinding() {
    
    cbTreeLaunchConfigs.setContentProvider(new LaunchConfigsTreeViewContentProvider());
    cbTreeLaunchConfigs.setLabelProvider(new LaunchConfigsTreeViewLabelProvider());
    cbTreeLaunchConfigs.setInput(mapAllLaunchConfigs.keySet().toArray());
  }
  
  class LaunchConfigsTreeViewContentProvider implements ITreeContentProvider {
    
    public void inputChanged(Viewer v, Object oldInput, Object newInput) {
    }

    public void dispose() {
    }

    public Object[] getElements(Object inputElement) {
      return (Object[]) inputElement;
    }

    public Object[] getChildren(Object parentElement) {
      String typeid = (String) parentElement;
      return getChildrenList(mapAllLaunchConfigs, typeid);
    }

    public Object getParent(Object element) {
      String typeid = (String) element;
      return typeid;
    }

    public boolean hasChildren(Object element) {
      String typeid = (String) element;
      return mapAllLaunchConfigs.containsKey(typeid);
    }

  }

  /**
   * Get the Launch Configuration type of a Launch Configuration.
   * 
   * @param configid The id of a Launch Configuration.
   * @return The type name of a Launch configuration.
   */
  private String findLaunchConfigType (String configid) {
    
    Set<String> keys = mapAllLaunchConfigs.keySet();
    Iterator<String> iterator = keys.iterator();
    String typeid = "";
    
    while (iterator.hasNext()) {
      String key = iterator.next();
      
      List<String> launchconfigs = mapAllLaunchConfigs.get(key);
      if (launchconfigs != null) {
        if (launchconfigs.contains(configid)) {
          typeid = key;
          break;
        }
      }
    }
    
    return typeid;
  }


  class LaunchConfigsTreeViewLabelProvider extends StyledCellLabelProvider {
    @Override
    public void update(ViewerCell cell) {
      Object element = cell.getElement();
      StyledString text = new StyledString();
      String key = (String) element;
      //Image image = imageLaunchNode;
      
      if (mapAllLaunchConfigs.containsKey(key)) {
        text.append(key);
        cell.setImage(getLaunchConfigNodeImage(key));
        Object[] projs = mapAllLaunchConfigs.get(key).toArray();
        if (projs != null) {
          text.append(" (" + projs.length + ") ",
              StyledString.COUNTER_STYLER);
        }
      } else {
        text.append(key);
        String typeid = findLaunchConfigType(key);
        cell.setImage(getLaunchConfigNodeImage(typeid));
        //cbTreeLaunchConfigs.setgray
      }
      
      cell.setText(text.toString());
      cell.setStyleRanges(text.getStyleRanges());
      super.update(cell);
  
    }
  
    public void setFocus() {
      cbTreeLaunchConfigs.getControl().setFocus();
    }
  } 
  
  /**
   * Get the corresponding image for a specific Launch Configuration type.
   * 
   * @param typeid The id of a Launch Configuration.
   * @return The image for the Launch Configuration.
   */
  private Image getLaunchConfigNodeImage(String typeid) {
    
    Image image = OTHER_LAUNCHES;
    // LaunchConfigTypes launchtypes;
    int typeindex = 0;
    
    for (LaunchConfigTypes type: LaunchConfigTypes.values()) {
      if (type.toString().equals(typeid)) {
        typeindex = type.ordinal() + 1;
        break;
      }
    }
    
    switch (typeindex) {
    case 1: 
      image = ECLIPSE_APP;
      break;
    case 2:
      image = JAVA_APPLET;
      break;
    case 3:
      image = JAVA_APP;
      break;
    case 4:
      image = JUNIT;
      break;
    case 5:
      image = JUNIT_PLUGIN_TEST;
      break;
    case 6:
      image = OSGI_FRAMEWORK;
      break;
    default:
      break;
    }
    
    return image;
  }
  
  private void initWorkingSetsTreeBinding() {
    
    cbTreeWorkingSets.setContentProvider(new WorkingSetsTreeViewContentProvider());
    cbTreeWorkingSets.setLabelProvider(new WorkingSetsTreeViewLabelProvider());
    cbTreeWorkingSets.setInput(mapAllWorkingSets.keySet().toArray());
  }
  
  class WorkingSetsTreeViewContentProvider implements ITreeContentProvider {
    
    public void inputChanged(Viewer v, Object oldInput, Object newInput) {
    }

    public void dispose() {
    }

    public Object[] getElements(Object inputElement) {
      return (Object[]) inputElement;
    }

    public Object[] getChildren(Object parentElement) {
      String setid = (String) parentElement;
      return getChildrenList(mapAllWorkingSets, setid);
    }

    public Object getParent(Object element) {
      String setid = (String) element;
      return setid;
    }

    public boolean hasChildren(Object element) {
      String setid = (String) element;
      return mapAllWorkingSets.containsKey(setid);
    }

  }
  
  private Object[] getChildrenList (Map<String, List<String>> map, String key) {
    
    Object[] chldlist = null;

    List<String> list = map.get(key);
    chldlist = list.toArray();

    return chldlist;
  }
  
  private Image getWorkingSetsNodeImage(boolean isparent) {
    
    Image image = PROJECT_ICON;
    
    if (true == isparent ) {

      image = WORKING_SETS_FOLD;
    } 
    else {
    }
    
    return image;
  }
  
  class WorkingSetsTreeViewLabelProvider extends StyledCellLabelProvider {
    @Override
    public void update(ViewerCell cell) {
      Object element = cell.getElement();
      StyledString text = new StyledString();
      String setid = (String) element;
      boolean isparent = false;
      
      if (mapAllWorkingSets.containsKey(setid)) {
        text.append(setid);
        isparent = true;
        Object[] projs = mapAllWorkingSets.get(setid).toArray();
        if (projs != null) {
          text.append(" (" + projs.length + ") ",
              StyledString.COUNTER_STYLER);
        }
      } else {
        text.append(setid);
      }
      cell.setImage(getWorkingSetsNodeImage(isparent));
      cell.setText(text.toString());
      cell.setStyleRanges(text.getStyleRanges());
      super.update(cell);

    }

    public void setFocus() {
      cbTreeWorkingSets.getControl().setFocus();
    }
  } 
  
  /**
   * The method is to check whether the current selected Task is for groups.
   * 
   */
  protected void isGroupTask(Shell pshell) {
    
    if (selTask.getDescription().contains("@grouptask")) {
      MessageDialog.openWarning(pshell, "Warning", "You are editing a"
          + " Task that is used by other developers as well!");
    }
  }
  
  /**
   * Write the current key/value pairs of all Tasks back to the Task files.
   * 
   * @throws IOException An exception will be thrown if fail to save the new
   * the new preferences to a specified file.
   *
   */
  private void writeEpfTaskFile (Task task) throws IOException {
    
    // for default: set the task type as: LAST_MODE
    int tasktype = 0;
    //String[] taskid = task.getId().split("@");
    String directory = getTaskPath(task.getId());
    IPath location = new Path(directory);

    if (task instanceof ReconcilingPreferencesTask) {
      tasktype = 1;
    }
    
    // Save the preferences into an EPF file
    EpfFileModel epfFile = new EpfFileModel(task.getTitle(), task.getDescription(), getTaskType(tasktype));   
    for (Map.Entry<String, String> entry : task.getKeyPairs().entrySet()) {
      epfFile.addElement(entry.getKey(), entry.getValue());
    }
    
    try {
      EpfFileModelWriter.write(epfFile, location);
    } catch (IOException e) {
      throw new IOException ("Error occurs while writing %s" + location + ": "
          + e.getMessage());
    }
    
  }
  
  /**
   * Get the full file path from the Task ID.
   *
   * @param taskid the id of the task file.
   * @return the full path of the task file.
   */
  private String getTaskPath (String taskid) {
    
    String[] str = taskid.split("@");
    String path = str[1];
    
    return path;
  }
  
  private TaskType getTaskType(int type) {
    return TaskType.values()[type];
  }
  
  /**
   * Create a file path with a specific extension. Normally the method is used
   * to create files that are to store information of Context elements, e.g.
   * Working Sets, Launch Configurations .etc.
   *
   * @param extension the file extension.
   * @return <code>file</code> a file name with a specified extension.
   * @throws IOException If an empty "file" with a specified extension couldn't
   * created, then throw the exception
   * 
   */
  private File getCtxElemFilePath (String extension) throws IOException {
    
    DefaultSysFilePath syspath = new DefaultSysFilePath();
    syspath.initCtxFileDir();
    String ctxpath = syspath.getDefaultSysFilePath();
    String filename = activCtxId + extension;
    
    Path path = new Path(filename);
    if (path.getFileExtension() == null) {
      filename = filename + extension;
    }
    
    String filepath =  ctxpath + activCtxId + File.separator + filename;
    File file = new File(filepath);
    try {
      file.createNewFile();
    } catch (IOException e) {
      throw new IOException ("Couldn't create the file: " + file + " "
          + e.getMessage());
    }
    
    return file;
  }
  
  
  @Override
  protected void okPressed () {
    
    activCtxId = "";
    
    super.okPressed();
  }
}
